/*
 * device.h
 *
 *  Created on: 28.06.2011
 *      Author: marcell
 */

#ifndef __DEVICE_H__
#define __DEVICE_H__

#include <export.h>
#include <ifile.h>
#include <iviewport.h>
#include <gfx/camera.h>
#include <gfx/capabilities.h>
#include <gfx/enum.h>
#include <gfx/idevice.h>
#include <gfx/iindexbuffer.h>
#include <gfx/irendertarget.h>
#include <gfx/itexture.h>
#include <gfx/ivertexbuffer.h>
#include <gfx/ivertexdeclaration.h>
#include <gfx/light.h>
#include <gfx/material.h>
#include <gfx/projector.h>
#include <gfx/sharedshaderbinding.h>
#include <math/boundingbox.h>
#include <math/matrix.h>
#include <string>

#define CE_CHECK_GL_ERROR \
{\
  GLenum error = glGetError ();\
  if (error != GL_NO_ERROR)\
    {\
      printf ("GL_ERROR: 0x%08x\n", error);\
      printf ("Error   : %s\n", (const char*)gluErrorString(error));\
      fflush (stdout);\
    }\
  assert(error == GL_NO_ERROR);\
}

/**
 * \addtogroup opengl
 * @{
 */
class ceVertexDeclaration;
/**
 * @brief OpenGL 2.0 implementation of the iDevice interface
 */
class CE_API ceDeviceGL20 : public iDevice
{
private:
  /**
   * @brief Combination of a shadow map texture and the respective transformation matrix.
   */
  struct ceShadowMap
	{
		iTexture*		ShadowMap;
    ceMatrix4f		Matrix;
	};


private:
  bool                  _capable;
  unsigned              _glslVersion;
  int                   _maxTextureCoordinates;
  int                   _maxTextureUnits;
  int                   _maxTextureSize;

  void checkCapabilities ();

  iVertexBuffer*        _vertexStreams[MaxStreams];
  iIndexBuffer*         _indices;
  ceVertexDeclaration*  _vertexDeclaration;
  iProgram*             _program;
	iEffectTechnique*       _effectTechnique;
  iRenderTarget*        _renderTarget;
  ceMatrix4f             _matrices[MS_Count];
  iTexture*             _textures[TS_Count];
  ceShadowMap						_shadowMap[3];
  iTexture*             _shadowOverlayer;
  ceMaterialSpec*           _material;
  ceLight*              _light;
	unsigned							_numLights;

  ceMatrix4f							_shadowBias;

  ceRenderPass					_pass;
  bool                  _useBlend;

  unsigned              _defaultAnisotropic;
  bool                  _defaultMipMap;
  ceFilter              _defaultFilter;
  ceWrapMode            _defaultWrapMode;
  ceCapabilities        _caps;


private:
  void BindMatrices ();

public:
  ceDeviceGL20 ();

  const ceCapabilities& GetCapabilities ();

  bool IsCapable ();
  void DebugStats ();

  bool CheckShadingVersion (unsigned version);
  bool CheckExtension (const char* extension);

  /**
   * \name Creation methods
   * @{
   */

  virtual ceRef<iVertexBuffer> CreateVertexBuffer (unsigned size, const void* data, ceBufferDataMode mode);
  virtual ceRef<iIndexBuffer> CreateIndexBuffer (unsigned size, const void* data, ceBufferDataMode mode);
  virtual ceRef<iVertexDeclaration> CreateVertexDeclaration (const ceVertexElement* elements);

  virtual ceRef<iEffect> CreateEffect ();
	virtual ceRef<iEffectTechnique> CreateEffectProgram ();
  virtual ceRef<iProgram> CreateProgram ();
  virtual ceRef<iShader> CreateShader (ceShaderType type) const;


  iTexture2D* CreateTexture2D (const ceTextureDescriptor& desc);
  iRenderTarget* CreateRenderTarget (const ceRenderTargetDescriptor& desc);
  /**
   * @}
   */

  /**
   * \name Functions for creating meshes
   * @{
   */
  virtual iBillboardMesh* CreateBillboardMesh ();
  virtual iInstancedMesh* CreateInstancedMesh ();
  virtual iMorphMesh* CreateMorphMesh ();
  virtual iPatchMesh* CreatePatchMesh ();
  virtual iSkinMesh* CreateSkinMesh ();
  virtual iStaticMesh* CreateStaticMesh ();
  /**
   * @}
   */



  /**
   * \name Defaults
   * @{
   */
  void SetDefaultAnisotropic (unsigned anisotropic);
  unsigned GetDefaultAnisotropic () const;

  void SetDefaultMipMap (bool mipMap);
  bool IsDefaultMipMap () const;

  void SetDefaultFilter (ceFilter filter);
  ceFilter GetDefaultFilter () const;

  void SetDefaultWrapMode (ceWrapMode wrapMode);
  ceWrapMode GetDefaultWrapMode () const;

  /**
   * @}
   */

	void SetEffectTechnique (iEffectTechnique* program);
	iEffectTechnique* GetEffectTechnique () const;
  void SetProgram (iProgram* program);
  iProgram* GetProgram () const;

  void ClearShadowMap (unsigned i);
  void SetShadowMap (unsigned i, iTexture* shadowMap, const ceMatrix4f& view, const ceMatrix4f& projection);

  void SetShadowOverlayer (iTexture* shadowOverlayer);


  void SetUseBlend(bool useBlend);

  void SetMaterial (ceMaterialSpec* material);
  void SetLight (ceLight* light);

  void ClearTextures ();
  void SetTexture (ceTextureStage stage, iTexture* texture);
  iTexture* GetTexture (ceTextureStage stage);

  void SetProjectionMatrix (const ceMatrix4f& matrix);
  void SetViewMatrix (const ceMatrix4f& matrix);
  void SetModelMatrix (const ceMatrix4f& matrix);

  bool BeginPass (ceRenderPass pass);
  void EndPass (ceRenderPass pass);


  void SetRenderTarget (iRenderTarget* renderTarget);

  void SetVertexDeclaration (iVertexDeclaration* vertexDeclaration);
  void SetStreamSource (unsigned stream, iVertexBuffer* buffer);
  void SetIndices(iIndexBuffer* indices);

  void ApplyShaderStates (ceSharedShaderBinding* bindings = 0);

  void DrawPrimitives(cePrimitiveType type, unsigned first, unsigned count, bool unbind = true);
  void DrawIndexPrimitives(cePrimitiveType pType, ceDataType iType, unsigned primCount, bool unbind = true);
  void DrawIndexPrimitivesInstanced(cePrimitiveType pType, ceDataType iType, unsigned primCount, unsigned instanceCount, bool unbind = true);
  void RedrawPrimitives (bool unbind = false);
  void RedrawIndexPrimitives (bool unbind = false);
  void DrawBoundingBox (const ceBoundingBox& bbox);
  void DrawRect (const ceRect& rect);
  void SetViewport (const iViewport* viewport);
  void SetViewport (const ceRect& viewport);
  void SetCamera (const ceCamera& camera);
  void SetProjector (const ceProjector& projector);
  void Clear (bool clearColor = true, const ceColor4f& color = ceColor4f(0, 0, 0, 1), bool clearDepth = true, float depth = 1.0f, bool clearStencil = false, unsigned stencil = 0);


};

/** @} */
#endif /* ! __DEVICE_H__ */
